// SocketWrapper.h
//
// Copyright (c) 2008 Bojan Rajkovic
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//

#ifndef _SOCKETWRAPPER_H_
#define _SOCKETWRAPPER_H_ 1

#include "AllPurpose.h"
#include "Utility.h"
#include <sys/socket.h>
#include <netdb.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>

#define LINE_BUFFER_SIZE 4096

namespace LinPulse {
	
		typedef unsigned char byte;
		
		class SocketException : public std::exception {
		
			private:
				int error;
				const char* message;
				
			public:
			
				SocketException(int Error) {
					this->error = Error;
					this->message = NULL;
					Utility::LogMessage(Utility::FormatString("SocketException: %s", what()), M_ERROR);
					
				}
				
				SocketException(const char* Message) {
					this->error = 0;
					this->message = Message;
				}
			
				virtual const char* what() const throw() {
					if (message == NULL) { 
						std::string whatMessage = Utility::FormatString("Got error %d: %s", error, strerror(error));
						return whatMessage.c_str();
					}
					else return message;
				}
		};
		
		class UNIXListenerSocket {
			
			private:
				int socketDescriptor, remoteSocketDescriptor;
				FILE* remoteSocketFileDescriptor;
				std::string socketLocation;
				
			public:
				
				// Constructors
				UNIXListenerSocket();
				UNIXListenerSocket(std::string);
				// Destructor
				~UNIXListenerSocket();
				// Helper function for the no-parameter constructor
				void Open(std::string);
				// Accept a connection
				void Accept();
				// Read until we've read int bytes.
				std::string Read(int);
				// Read a line from the socket--ie. until we reach \n,
				// We'll have to do some guessing at the length of the response,
				// but a buffer size of 4 KiB should be sufficient.
				std::string ReadLine();
				// Read n bytes and return them as a byte[].
				byte* ReadBytes(int);
				// Write the string to the socket, return how many bytes we wrote
				int Write(std::string);
				// Write the data to the socket, then a newline
				int WriteLine(std::string);
				// Get the hostname we're connected to
				inline std::string SocketLocation() { return this->socketLocation; }
				// Get the socket descriptor (the return value from socket())
				inline int SocketDescriptor() { return this->socketDescriptor; }
				// Get the socket descriptor for the remote socket (from accept())
				inline int RemoteSocketDescriptor() { return this->remoteSocketDescriptor; }
				// Close the remote socket
				inline void CloseRemoteSocket() { 
					close(this->remoteSocketDescriptor);
					fclose(this->remoteSocketFileDescriptor);
				}
		};

		class IPv4ClientSocket {
			
			private:
				int socketDescriptor;
				FILE* socketFileDescriptor;
				std::string hostName;
				unsigned short hostPort;
				
			public:
				
				// Constructors
				IPv4ClientSocket();
				IPv4ClientSocket(std::string, unsigned int);
				// Destructor
				~IPv4ClientSocket();
				// Helper function for when we use the no-parameter constructor
				void Open(std::string, unsigned int);
				// Read until we've read int bytes.
				std::string Read(int);
				// Read a line from the socket--ie. until we reach \n,
				// We'll have to do some guessing at the length of the response,
				// but a buffer size of 4 KiB should be sufficient.
				std::string ReadLine();
				// Read n bytes and return them as a byte[].
				byte* ReadBytes(int);
				// Write the string to the socket, return how many bytes we wrote
				int Write(std::string);
				// Write the data to the socket, then a newline
				int WriteLine(std::string);
				// Get the hostname we're connected to
				std::string GetHostName() { return this->hostName; }
				// Get the host port we're connected to
				unsigned short GetHostPort() { return this->hostPort; }
				// Get the FILE* descriptor for the socket
				FILE* FileDescriptor() { return this->socketFileDescriptor; }
				// Get the socket file descriptor (the return value from socket())
				int SocketDescriptor() { return this->socketDescriptor; }
				
		};
}

using namespace LinPulse;

#endif
